package cn.iocoder.yudao.demo.redis.controller.admin;

import lombok.RequiredArgsConstructor;
import org.redisson.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Arrays;
import java.util.Collections;
import java.util.StringJoiner;

@Primary
@RequiredArgsConstructor
@RestController
@RequestMapping("/redis")
public class RedisController {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @GetMapping
    public String redissonTest() {

        // *** string
        ValueOperations<String, Object> strValOps = redisTemplate.opsForValue();
        strValOps.set("kstring1", "k1111111");


        // *** list
        ListOperations<String, Object> strListOps = redisTemplate.opsForList();
        for (int i = 0; i < 10; i++) {
            strListOps.rightPush("klist1", "瓜田李下" + i);
        }

        // *** set
        SetOperations<String, Object> strSetOps = redisTemplate.opsForSet();
        for (int i = 0; i < 10; i++) {
            strSetOps.add("kset1", "瓜田李下" + i);
        }

        // *** zset
        ZSetOperations<String, Object> strZSetOps = redisTemplate.opsForZSet();
        for (int i = 0; i < 10; i++) {
            strZSetOps.add("kzset1", "瓜田李下" + i, i);
        }

        // *** map
        HashOperations<String, Object, Object> strObjHashOps = redisTemplate.opsForHash();
        for (int i = 0; i < 10; i++) {
            strObjHashOps.put("kmap1", i+"", "瓜田李下" + i);
        }

        // *** 使用Redis事务
        // 事务特性（原子性，一致性，隔离性，持久性）
        // Redis事务有一些限制和缺陷：
        //不支持回滚机制：Redis 事务虽然提供了事务的功能，但是不支持事务的回滚（ROLLBACK）操作。如果在事务执行过程中发生了错误，只能通过 DISCARD 命令取消事务，无法回滚到之前的状态。
        //不支持原子性操作之外的命令：Redis 事务仅对支持原子性操作的命令（例如 SET、GET、DEL 等）起作用。对于像 WATCH、MULTI、EXEC、DISCARD 这样的事务控制命令本身，不是原子性的。
        //不是严格的事务隔离级别：Redis 的事务隔离性不像关系型数据库那样严格。如果多个事务同时修改同一键的值，后提交的事务会覆盖先提交的事务，这可能导致数据不一致的问题。
        //执行事务期间阻塞其他客户端：在执行事务期间，Redis 会阻塞其他客户端的请求，直到当前事务执行完成。如果事务执行时间过长，会导致其他客户端的请求排队等待，降低了系统的并发性能。
        //事务性能较低：Redis 的事务是单线程执行的，事务中的命令是顺序执行的，这限制了事务的并发性能。如果事务中包含大量的命令或者复杂的计算，可能会导致事务执行时间过长，影响系统的性能表现。

        redisTemplate.execute(new SessionCallback<Object>() {
            @Override
            public Object execute(RedisOperations operations) throws DataAccessException {
                operations.multi(); //开启事务
                ValueOperations valueOps = operations.opsForValue();
                valueOps.set("key1", "value1");
                valueOps.set("key2", "value2");
                return operations.exec(); // 提交事务
            }
        });

        // *** 使用Lua脚本
        StringJoiner luaScript = new StringJoiner(" ");
        luaScript //
                .add("redis.call('SET', KEYS[1], ARGV[1]);")
                .add("redis.call('SET', KEYS[2], ARGV[2]);")
                ;
        DefaultRedisScript<Long> script = new DefaultRedisScript<>(luaScript.toString() /*脚本*/, Long.class /*返回值类型*/);
        Long execute = redisTemplate.execute(script, Arrays.asList("key1", "key2"), "value1", "value2");


        // 使用Redis的分布式锁
        return "ok";
    }
}
